/*-------------------------------------------------------------------------
 *
 * tupmacs.h
 *	  用于索引元组和堆元组的元组宏。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/tupmacs.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef TUPMACS_H
#define TUPMACS_H

#include "catalog/pg_type_d.h"	/* 用于 TYPALIGN 宏 */


/*
 * 检查元组的空位位图以确定属性是否为空。
 * 注意，空位位图中的 0 表示空，而 1 表示非空。
 */
#define att_isnull(ATT, BITS) (!((BITS)[(ATT) >> 3] & (1 << ((ATT) & 0x07))))

/*
 * 给定一个 Form_pg_attribute 和一个指向元组数据区域的指针，
 * 返回正确的值或指针。
 *
 * 在所有情况下我们返回一个 Datum 值。如果属性的 "byval" 为假，
 * 我们返回传入的元组数据区域中的相同指针。
 * 否则，我们返回从数据区域获取的正确字节数，并扩展为 Datum 格式。
 *
 * 在 Datum 为 8 字节的机器上，我们支持获取 8 字节的 byval 属性；
 * 否则，仅支持 1、2 和 4 字节的值。
 *
 * 注意，T 必须已经正确对齐才能正确工作。
 */
#define fetchatt(A,T) fetch_att(T, (A)->attbyval, (A)->attlen)

/*
 * 相同，但从 byval/len 参数工作，而不是 Form_pg_attribute。
 */
#if SIZEOF_DATUM == 8

#define fetch_att(T,attbyval,attlen) \
( \
	(attbyval) ? \
	( \
		(attlen) == (int) sizeof(Datum) ? \
			*((Datum *)(T)) \
		: \
	  ( \
		(attlen) == (int) sizeof(int32) ? \
			Int32GetDatum(*((int32 *)(T))) \
		: \
		( \
			(attlen) == (int) sizeof(int16) ? \
				Int16GetDatum(*((int16 *)(T))) \
			: \
			( \
				AssertMacro((attlen) == 1), \
				CharGetDatum(*((char *)(T))) \
			) \
		) \
	  ) \
	) \
	: \
	PointerGetDatum((char *) (T)) \
)
#else							/* SIZEOF_DATUM != 8 */

#define fetch_att(T,attbyval,attlen) \
( \
	(attbyval) ? \
	( \
		(attlen) == (int) sizeof(int32) ? \
			Int32GetDatum(*((int32 *)(T))) \
		: \
		( \
			(attlen) == (int) sizeof(int16) ? \
				Int16GetDatum(*((int16 *)(T))) \
			: \
			( \
				AssertMacro((attlen) == 1), \
				CharGetDatum(*((char *)(T))) \
			) \
		) \
	) \
	: \
	PointerGetDatum((char *) (T)) \
)
#endif							/* SIZEOF_DATUM == 8 */

/*
 * att_align_datum 根据对齐要求 attalign 和 类型长度 attlen 需要
 * 对给定偏移量进行对齐。attdatum 是我们打算打包到元组中的 Datum 变量
 * （仅在处理 varlena 类型时访问）。注意，这假定 Datum 将按原样存储；
 * 旨在将非短 varlena datums 转为短格式的调用者必须自行考虑这一点。
 */
#define att_align_datum(cur_offset, attalign, attlen, attdatum) \
( \
	((attlen) == -1 && VARATT_IS_SHORT(DatumGetPointer(attdatum))) ? \
	(uintptr_t) (cur_offset) : \
	att_align_nominal(cur_offset, attalign) \
)

/*
 * att_align_pointer 执行与 att_align_datum 相同的计算，
 * 但在遍历元组时使用。attptr 是当前实际数据指针；
 * 在访问 varlena 字段时，我们必须 "窥视" 以查看我们是否在查看
 * 填充字节，还是一个字节头 datum 的第一个字节。
 * （零字节必须是填充字节，或正确对齐的 4 字节长度字的第一个字节；
 * 在这两种情况下，我们可以安全对齐。非零字节必须是 1 字节长度字，
 * 或正确对齐的 4 字节长度字的第一个字节；在这两种情况下我们不需要对齐。）
 *
 * 注意：一些调用者为 cur_offset 传递 "char *" 指针。
 * 这虽然是一种 hack，但只要 uintptr_t 的宽度是正确的就应该没问题。
 */
#define att_align_pointer(cur_offset, attalign, attlen, attptr) \
( \
	((attlen) == -1 && VARATT_NOT_PAD_BYTE(attptr)) ? \
	(uintptr_t) (cur_offset) : \
	att_align_nominal(cur_offset, attalign) \
)

/*
 * att_align_nominal 根据对齐要求 attalign 所需的方式对给定偏移量进行对齐，
 * 忽略任何对打包 varlena datums 的考虑。
 * 直接使用这个宏的主要用例有三个：
 *	* 我们知道相关的 att 不是 varlena（attlen != -1）；
 *	  在这种情况下，它比上述宏便宜且同样有效。
 *	* 我们需要抽象地估计对齐填充成本，即不参考真实的元组。
 *	  我们必须假设最坏情况下所有 varlenas 都是对齐的。
 *	* 在数组和多范围内，我们无条件对齐 varlenas（XXX 这
 *	  应该重新审视，可能）。
 *
 * attalign 的情况是在希望它们的发生频率类似的情况下进行测试的。
 */
#define att_align_nominal(cur_offset, attalign) \
( \
	((attalign) == TYPALIGN_INT) ? INTALIGN(cur_offset) : \
	 (((attalign) == TYPALIGN_CHAR) ? (uintptr_t) (cur_offset) : \
	  (((attalign) == TYPALIGN_DOUBLE) ? DOUBLEALIGN(cur_offset) : \
	   ( \
			AssertMacro((attalign) == TYPALIGN_SHORT), \
			SHORTALIGN(cur_offset) \
	   ))) \
)

/*
 * att_addlength_datum 根据给定 Datum 变量所需的空间增加给定偏移量。
 * attdatum 仅在处理可变长度属性时访问。
 */
#define att_addlength_datum(cur_offset, attlen, attdatum) \
	att_addlength_pointer(cur_offset, attlen, DatumGetPointer(attdatum))

/*
 * att_addlength_pointer 执行与 att_addlength_datum 相同的计算，
 * 但在遍历元组时使用 --- attptr 是元组内字段的指针。
 *
 * 注意：一些调用者为 cur_offset 传递 "char *" 指针。
 * 这实际上是完全可以的，但可能应该与 att_align_pointer 的
 * 相同做法一起清理。
 */
#define att_addlength_pointer(cur_offset, attlen, attptr) \
( \
	((attlen) > 0) ? \
	( \
		(cur_offset) + (attlen) \
	) \
	: (((attlen) == -1) ? \
	( \
		(cur_offset) + VARSIZE_ANY(attptr) \
	) \
	: \
	( \
		AssertMacro((attlen) == -2), \
		(cur_offset) + (strlen((char *) (attptr)) + 1) \
	)) \
)

/*
 * store_att_byval 是 fetch_att 的部分逆操作：将给定的 Datum
 * 值存储到指定地址的元组数据区域中。然而，它只处理 byval 情况，
 * 因为在典型使用中，调用者需要区分 by-val 和 by-ref 情况，
 * 所以一个全能宏并不方便。
 */
#if SIZEOF_DATUM == 8

#define store_att_byval(T,newdatum,attlen) \
	do { \
		switch (attlen) \
		{ \
			case sizeof(char): \
				*(char *) (T) = DatumGetChar(newdatum); \
				break; \
			case sizeof(int16): \
				*(int16 *) (T) = DatumGetInt16(newdatum); \
				break; \
			case sizeof(int32): \
				*(int32 *) (T) = DatumGetInt32(newdatum); \
				break; \
			case sizeof(Datum): \
				*(Datum *) (T) = (newdatum); \
				break; \
			default: \
				elog(ERROR, "unsupported byval length: %d", \
					 (int) (attlen)); \
				break; \
		} \
	} while (0)
#else							/* SIZEOF_DATUM != 8 */

#define store_att_byval(T,newdatum,attlen) \
	do { \
		switch (attlen) \
		{ \
			case sizeof(char): \
				*(char *) (T) = DatumGetChar(newdatum); \
				break; \
			case sizeof(int16): \
				*(int16 *) (T) = DatumGetInt16(newdatum); \
				break; \
			case sizeof(int32): \
				*(int32 *) (T) = DatumGetInt32(newdatum); \
				break; \
			default: \
				elog(ERROR, "unsupported byval length: %d", \
					 (int) (attlen)); \
				break; \
		} \
	} while (0)
#endif							/* SIZEOF_DATUM == 8 */

#endif
